package com.tian.arch.function.adapter.diff

import androidx.recyclerview.widget.DiffUtil

/**
 * 用于比较 2个数据源之间的差异，得到最小更新集后的回调
 *
 * ```
 * 1、DiffUtil的简单使用
 *
 * val diffResult = DiffUtil.calculateDiff(new LiteDiffUtil(oldData,newData,diffCallback),true)
 * diffResult.dispatchUpdatesTo(recyclerAdapter)
 *
 * 2、实现差量更新(数据更新后，某些item内容改变)
 *
 *   1) 重写"getChangePayload", 比对新旧数据的差异并放入到一个[Bundle]中，最后返回
 *   val callback = object:DiffUtil.Callback() {
 *      ...
 *      fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
 *          val oldUser = oldData[oldItemPosition]
 *          val newUser = newData[newItemPosition]
 *          val payload = Bundle()
 *
 *          if(oldUser.name != newUser.name) {
 *              payload.putString("name", newUser.name)
 *          }
 *          ...
 *          return payload
 *      }
 *   }
 *
 *   2) adapter重新 onBindViewHolder, 利用上前面返回的Bundle数据，注意一定是带3个参数的那个方法
 *   class MAdapter .. {
 *         // 一般我们实现的 onBindViewHolder
 *         override fun onBindViewHolder(holder: ViewHolder, position: Int) {
 *            ...
 *         }
 *
 *         // 为做到差量更新，这里需要再实现一个 3个参数的onBindViewHodler
 *         override fun onBindViewHolder(holder: ViewHolder, position: Int， payloads:MutableList<Any>) {
 *             if(payloads.isEmpty()) {
 *                  onBindViewHolder(holder,postion)
 *                  return
 *             }
 *
 *             // 有效变化
 *             val payload = payloads[0] as Bundle
 *             val newName = payload.getString("name")
 *             holder.name.text = newName
 *         }
 *         ...
 *   }
 *
 *   3) 当明确的知道某个项的变化时
 *      class MActivity ... {
 *
 *          fun onClick() {
 *              // 数据中的第6项，名字发生变化
 *              val user = myAdapter.data[position]
 *              user.name = "Jender Cc"
 *
 *              // 构建有效变化
 *              val payload = Bundle()
 *              payload.putString("name",user.name)
 *              myAdapter.notifyItemChanged(position, payload)
 *          }
 *           ...
 *      }
 * ```
 *
 */
class LiteDiffUtil(
    private val oldData: List<*>?,
    private val newData: List<*>?,
    private val diffCallback: Callback
) : DiffUtil.Callback() {

    override fun getOldListSize(): Int {
        return oldData?.size ?: 0
    }

    override fun getNewListSize(): Int {
        return newData?.size ?: 0
    }


    /**
     *  判断2个对象是否 表示同一个item项，通常实现为比较2个数据的id
     */
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return diffCallback.areItemsTheSame(oldData!![oldItemPosition], newData!![newItemPosition])
    }

    /**
     *  [areItemsTheSame]返回true时，判断2个item数据内容是否一样
     */
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return diffCallback.areContentsTheSame(oldData!![oldItemPosition], newData!![newItemPosition])
    }

    /**
     *  获取改变后的有效数据装载
     */
    override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
        // 当areItemsTheSame = true，areContentsTheSame = false时调用
        // 返回两个item内容差异的字段，在adapter的三个参数的onBindViewHolder方法中，可以实现差量更新
        return diffCallback.getChangePayload(oldItemPosition, newItemPosition)
    }

    interface Callback {
        fun areItemsTheSame(oldItem: Any?, newItem: Any?): Boolean
        fun areContentsTheSame(oldItem: Any?, newItem: Any?): Boolean
        fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any?
    }
}